home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / MoreKittiesPlease / Source / Module.c < prev   
Encoding:
Text File  |  2000-06-23  |  16.8 KB  |  595 lines

  1.  
  2. #import "DebugFlags.h"
  3.  
  4. #import <OpenTptModule.h>
  5. #import <OTDebug.h>
  6. #import "OTDCon.h"
  7. #import "Kitties_Module.h"
  8.  
  9. #import <dlpi.h>
  10. #import <mistream.h>
  11.  
  12. #import "DLPIUtils.h"
  13. static char*     gStreamList = nil;
  14. static    UInt32    gFlags        = nil;
  15.  
  16. enum {
  17.     kStateInit,
  18.     kStateSawCR1,
  19.     kStateSawLF1,
  20.     kStateSawCR2,
  21.     kStateLookingForBody,
  22.     kStateInBody,
  23.     kStateInAngle,
  24.     kStateInBodyAngle,
  25.     kStateNotHTML,
  26.     kStateSkipContentLength
  27.     };
  28.  
  29. static Boolean IsReadQ(queue_t* q)
  30.     // Returns true if q is the read queue of a queue pair.
  31. {
  32.     return ( (q->q_flag & QREADR) != 0 );
  33. }
  34.  
  35. static Boolean IsWriteQ(queue_t* q)
  36.     // Returns true if q is the write queue of a queue pair.
  37. {
  38.     return ( (q->q_flag & QREADR) == 0 );
  39. }
  40.  
  41. struct PerStreamData
  42. {
  43.     OSType magic;                // kQTunnelPerStreamDataMagic for debugging
  44.     UInt32 currentState;        // Current DLPI state, ie DL_UNATTACHED etc
  45.     queue_t* readQ;                // Read queue of this stream
  46.     UInt8    http;
  47.     UInt8    state;
  48.     UInt8    lastHeader;
  49.     major_t major;
  50.     minor_t minor;
  51. };
  52. typedef struct PerStreamData PerStreamData, *PerStreamDataPtr;
  53.  
  54.  
  55. struct MoreKittiesPortData {
  56.     OSType magic;            // Should be kQTunnelPortMagic
  57.     // [... extra info for port goes here ...]
  58. };
  59. typedef struct MoreKittiesPortData MoreKittiesPortData;
  60.  
  61.  
  62. static PerStreamDataPtr GetPerStreamData(queue_t* readOrWriteQ)
  63.     // You can pass both the read or the write queue to this routine
  64.     // because mi_open_comm sets up both q_ptr's to point to the
  65.     // queue local data.
  66.     //
  67.     // Note that, in order to avoid the overhead of a function call,
  68.     // you would normally use inline code (or a macro)
  69.     // to get your per-stream data instead of using a separate function.
  70.     // However I think the separate function makes things clearer.
  71.     // I also acts as a central bottleneck for my debugging code.
  72.     //
  73.     // Environment: any standard STREAMS entry point
  74. {
  75.     PerStreamDataPtr streamData;
  76.     
  77.     streamData = (PerStreamDataPtr) readOrWriteQ->q_ptr;
  78.  
  79.     OTAssert("GetPerStreamData: what streamData", streamData != nil);
  80.     OTAssert("GetPerStreamData: Bad magic", streamData->magic == kQTunnelPerStreamDataMagic);
  81.     
  82.     return (streamData);
  83. }
  84.  
  85. void *SetFilter( UInt32 flags )
  86. {
  87.     gFlags    = flags;    
  88. }
  89.  
  90.  
  91. void
  92. ShouldDecode( void *buffer, UInt32 length, void *userData )
  93. {
  94.     PerStreamData    *streamData;
  95.     
  96.     if ( !length ) {
  97.         streamData = GetPerStreamData((queue_t *)userData);
  98.         streamData->http    = false;
  99.     }
  100. }
  101.  
  102. static int MoreKittiesWritePut(queue_t* q, mblk_t* mp)
  103.     // This routine is called by STREAMS when it has a message for our
  104.     // module from upstream.  Typically, this routine is a big case statement
  105.     // that dispatches to our various message handling routines.
  106.     //
  107.     // Environment: standard STREAMS entry point
  108. {
  109.     PerStreamData *streamData;
  110.     mblk_t            *parse_blk;
  111.     OTAssert("MoreKittiesWritePut: Not the write queue", IsWriteQ(q) );
  112. //    Dump_Packet_Type( "\n===>MoreKittiesWritePut", mp );
  113. //    Dump_Packet( mp );
  114.     streamData = GetPerStreamData(q);
  115.     
  116.     putnext( q, mp );    
  117.     return 0;
  118. }
  119.  
  120. static void Output( queue_t *q, char *output, UInt32 outputLength )
  121. {
  122.     mblk_t *mp = 0;
  123.     mp = allocb( outputLength, 0 );
  124.     BlockMoveData( output, mp->b_rptr, outputLength );
  125.     mp->b_wptr = mp->b_rptr + outputLength;
  126.     putnext( q, mp );
  127. }
  128.  
  129. #define hamster1string "<img src=\"http://www.stattenfield.org/humor/www.hamsterdance.com/hamu.gif\">\n"
  130. #define hamster2string "<img src=\"http://www.stattenfield.org/humor/www.hamsterdance.com/anin.gif\">\n"
  131. #define hamster3string "<img src=\"http://www.stattenfield.org/humor/www.hamsterdance.com/gerbil.gif\">\n"
  132. #define hamster4string "<img src=\"http://www.stattenfield.org/humor/www.hamsterdance.com/hamwalk.gif\">\n"
  133.  
  134. #define kitten1string "<img src=\"http://www.tweekitten.com/Graphics/tkcat.gif\">\n"
  135. #define kitten2string "<img src=\"http://www.pets.com/images/ui/persian6_if_061900.jpg\">\n"
  136.  
  137. static void SendHamster( queue_t *q )
  138. {
  139.     if ( (gFlags & kHamsters) && !(rand() % 10) ) {
  140.         switch ( rand() %4 ) {
  141.             case 0:        Output( q, hamster1string, sizeof(hamster1string) ); break;
  142.             case 1:        Output( q, hamster2string, sizeof(hamster2string) ); break;
  143.             case 2:        Output( q, hamster3string, sizeof(hamster3string) ); break;
  144.             case 3:        Output( q, hamster4string, sizeof(hamster4string) ); break;
  145.         }
  146.     }
  147.     if ( (gFlags & kKittiesFlag) && !(rand() % 10) ) {
  148.         switch ( rand() %2 ) {
  149.             case 0:        Output( q, kitten1string, sizeof(kitten1string) ); break;
  150.             case 1:        Output( q, kitten2string, sizeof(kitten2string) ); break;
  151.         }
  152.     }
  153. }
  154.  
  155. #define    header1String    "<b>"
  156. #define header1String2    "</b>"
  157. #define    header2String    "<i>"
  158. #define header2String2    "</i>"
  159. #define    header3String    "<H1>"
  160. #define header3String2    "</H1>"
  161. #define    header4String    "<H6>"
  162. #define header4String2    "</H6>"
  163. #define    header5String    "<underline>"
  164. #define header5String2    "</underline>"
  165.  
  166. static void SendHeader( queue_t *q, PerStreamData *streamData )
  167. {
  168.     if ( streamData->lastHeader ) {
  169.         switch( streamData->lastHeader ) {
  170.             case 1:    Output( q, header1String2, sizeof(header1String2) );    break;
  171.             case 2:    Output( q, header2String2, sizeof(header2String2) );    break;
  172.             case 3:    Output( q, header3String2, sizeof(header3String2) );    break;
  173.             case 4:    Output( q, header4String2, sizeof(header4String2) );    break;
  174.             case 5:    Output( q, header5String2, sizeof(header5String2) );    break;
  175.         }
  176.     }
  177.     if ( (gFlags & kHeaders) )  {
  178.         streamData->lastHeader = rand() % 5 + 1;
  179.         switch( streamData->lastHeader ) {
  180.             case 1:    Output( q, header1String, sizeof(header1String) );    break;
  181.             case 2:    Output( q, header2String, sizeof(header2String) );    break;
  182.             case 3:    Output( q, header3String, sizeof(header3String) );    break;
  183.             case 4:    Output( q, header4String, sizeof(header4String) );    break;
  184.             case 5:    Output( q, header5String, sizeof(header5String) );    break;
  185.         }
  186.     }
  187. }
  188.  
  189. static void Parse( queue_t *q, PerStreamData *streamData, unsigned char *data, UInt32 length ) 
  190. {
  191.     UInt32    count;
  192.     
  193.     char    output[1460];
  194.     UInt32    outputLength;
  195.     char    contentLength[150];
  196.     UInt32    contentLengthLength;
  197.         
  198.     outputLength = 0;
  199.     if ( !strncmp( "HTTP/1", data, 6 ) ) {
  200.         streamData->state = kStateInit;
  201.     }
  202.     
  203.     for ( count = 0; count < length; count++ ) {
  204.         switch ( streamData->state ) {
  205.             case kStateInit:
  206.                 if ( data[count] == 13 ) {
  207.                     streamData->state         = kStateSawCR1;
  208.                 }
  209.                 output[outputLength++]    = data[count];
  210.                 break;
  211.             case kStateSawCR1:
  212.                 if ( data[count] == 10 ) {
  213.                     streamData->state         = kStateSawLF1;
  214.                 } else {
  215.                     streamData->state         = kStateInit;
  216.                 }
  217.                 output[outputLength++]    = data[count];
  218.                 break;
  219.             case kStateSawLF1:
  220.                 //dprintmem( data+count, 20 );
  221.                 if ( data[count] == 'C' ) {
  222.                     if ( !strncmp( data+count, "Content-Type: ", 14 ) ) {
  223.                         if ( strncmp( data+count+14, "text/html", 9 ) ) {
  224.                             streamData->state = kStateNotHTML;
  225.                         } else {
  226.                             streamData->state = kStateInit;
  227.                         }
  228.                     } else if ( !strncmp( data+count, "Content-Length: ", 16 ) ) {
  229. //                        streamData->state = kStateSkipContentLength;
  230.                         contentLengthLength = 0;
  231.                         contentLength[contentLengthLength++]    = data[count];
  232. //                        continue;
  233.                     }
  234.                 } else if ( data[count] == 13 ) {
  235.                     streamData->state         = kStateSawCR2;
  236.                 } else {
  237.                     streamData->state         = kStateInit;
  238.                 }
  239.                 output[outputLength++]    = data[count];
  240.                 break;
  241.             case kStateSawCR2:
  242.                 if ( data[count] == 10 ) {
  243.                     streamData->state         = kStateLookingForBody;
  244.                 } else {
  245.                     streamData->state         = kStateInit;
  246.                 }
  247.                 output[outputLength++]    = data[count];
  248.                 if ( outputLength >= 1460 ) {
  249.                     Output( q, output, outputLength );
  250.                     outputLength = 0;
  251.                 }
  252.                 break;
  253.             case kStateLookingForBody:
  254.                 if ( (data[count] == '<') && 
  255.                         (count < length - 6)) {
  256.                     if ( ((data[count+1] == 'B') || (data[count+1] == 'b')) &&
  257.                             ((data[count+2] == 'O') || (data[count+2] == 'o')) &&
  258.                             ((data[count+3] == 'D') || (data[count+3] == 'd')) &&
  259.                             ((data[count+4] == 'Y') || (data[count+4] == 'y')) ) {
  260.                         streamData->state = kStateInBodyAngle;
  261.                     }
  262.                     
  263.                 }
  264.                 output[outputLength++]    = data[count];
  265.                 if ( outputLength >= 1460 ) {
  266.                     Output( q, output, outputLength );
  267.                     outputLength = 0;
  268.                 }
  269.                 break;
  270.             case kStateInBodyAngle:
  271.                 output[outputLength++]    = data[count];
  272.                 switch ( data[count] ) {
  273.                     case '>': 
  274.                         Output( q, output, outputLength );
  275.                         outputLength = 0;
  276.                         streamData->state = kStateInBody;
  277.                         SendHamster( q );
  278.                         break;
  279.                 }
  280.                 if ( outputLength >= 1460 ) {
  281.                     Output( q, output, outputLength );
  282.                     outputLength = 0;
  283.                 }
  284.                 break;                
  285.             case kStateInBody:
  286.                 switch ( data[count] ) {
  287.                     case '<': streamData->state = kStateInAngle;    break;
  288.                     case ' ':
  289.                         output[outputLength++]    = data[count];
  290.                         Output( q, output, outputLength );
  291.                         outputLength = 0;
  292.                         streamData->state = kStateInBody;    
  293.                         SendHeader( q, streamData );
  294.                         break;
  295.                 }
  296.                 output[outputLength++]    = data[count];
  297.                 if ( outputLength >= 1460 ) {
  298.                     Output( q, output, outputLength );
  299.                     outputLength = 0;
  300.                 }
  301.                 break;
  302.             case kStateInAngle:
  303.                 switch ( data[count] ) {
  304.                     case '>': 
  305.                         output[outputLength++]    = data[count];
  306.                         Output( q, output, outputLength );
  307.                         outputLength = 0;
  308.                         streamData->state = kStateInBody;    
  309.                         SendHamster( q );
  310.                         break;
  311.                     default:
  312.                         output[outputLength++]    = data[count];
  313.                         break;
  314.                 }
  315.                 break;                
  316.             case kStateNotHTML:
  317.                 output[outputLength++]    = data[count];
  318.                 if ( outputLength >= 1460 ) {
  319.                     Output( q, output, outputLength );
  320.                     outputLength = 0;
  321.                 }
  322.                 break;
  323.             case kStateSkipContentLength:
  324.                 contentLength[contentLengthLength++]    = data[count];
  325.                 if ( data[count] == 13 ) {
  326.                     count++;
  327.                     streamData->state = kStateInit;
  328.                 }
  329.         }
  330.     }
  331.     Output( q, output, outputLength );
  332.     outputLength = 0;
  333.  
  334. }
  335.  
  336. static int MoreKittiesReadPut(queue_t* q, mblk_t* mp)
  337.     // This routine is called by STREAMS when it has a message for our
  338.     // module from downstream.  Typically, this routine is a big case statement
  339.     // that dispatches to our various message handling routines.
  340.     //
  341.     // Environment: standard STREAMS entry point
  342. {
  343.     PerStreamData     *streamData;
  344.     mblk_t            *parse_blk;
  345.     T_conn_con        *conCon;
  346.     
  347.     OTAssert("MoreKitties: Not the read queue", IsReadQ(q) );
  348.     //Dump_Packet_Type( "\n<---MoreKittiesReadPut", mp );
  349.     //Dump_Packet( mp );
  350.     streamData = GetPerStreamData(q);
  351.     
  352.     switch (mp->b_datap->db_type) {
  353.         case M_PCPROTO:
  354.         case M_PROTO:
  355.             switch ( ( (union T_primitives*) mp->b_rptr)->type ) {
  356.                 case T_CONN_CON:
  357.                     conCon    = (T_conn_con *)mp->b_rptr;
  358.                     if ( conCon && (conCon->RES_length) ) {
  359.                         if ( *(UInt16 *)(mp->b_rptr + conCon->RES_offset + 2 ) == 80 ) {
  360.                             streamData->http = true;
  361.                             streamData->state = kStateInit;
  362.                         } else {
  363.                         }
  364.                     }
  365.                     putnext( q, mp );
  366.                     break;
  367.                 default:
  368.                     putnext( q, mp );
  369.                     break;
  370.             }
  371.             break;
  372.         case M_DATA:
  373.             if ( streamData->http ) {
  374.                 for( parse_blk = mp; parse_blk; parse_blk=parse_blk->b_cont ) {
  375.                     Parse( q,
  376.                             streamData, 
  377.                             parse_blk->b_rptr, 
  378.                             parse_blk->b_wptr-parse_blk->b_rptr );
  379.                 }
  380.                 freemsg( mp );
  381.             } else {
  382.                 putnext( q , mp );
  383.             }
  384.             break;
  385.         default:
  386.             putnext(q, mp);
  387.             break;
  388.     }
  389.     
  390.     return 0;
  391. }
  392.  
  393. /////////////////////////////////////////////////////////////////////
  394. // Open routine
  395.  
  396. static int MoreKittiesOpen(queue_t* rdq, dev_t* dev, int flag, int sflag, cred_t* creds)
  397.     // This routine is called by STREAMS when a new stream is connected to
  398.     // our module.  The bulk of the work here is done by the Mentat helper
  399.     // routine mi_open_comm.
  400.     //
  401.     // Environment: standard STREAMS entry point
  402. {
  403.     int err;
  404.     PerStreamDataPtr streamData;
  405.     
  406.     OTAssert("MoreKittiesOpen: Not the read queue", IsReadQ(rdq) );
  407.  
  408.     EntryPoint("MoreKittiesOpen\n");
  409.     
  410.     err = noErr;
  411.     
  412.     // If we already have per-stream data for this stream, the stream is being reopened.
  413.     // In that case, we can just return.
  414.     // Note that we can't call GetPerStreamData because it checks that streamData is not nil.
  415.     
  416.     if ( rdq->q_ptr != nil ) {
  417.         goto done;
  418.     }
  419.  
  420.     // Make sure we're being opened properly -- because we're a module we
  421.     // require a MODOPEN.
  422.     
  423.     if ( (err == noErr) && (sflag != MODOPEN) ) {
  424.         err = ENXIO;
  425.     }
  426.     
  427.     // Use the mi_open_comm routine to allocate our per-stream data.  Then
  428.     // zero out the entire per-stream data record and fill out the fields
  429.     // we're going to need.
  430.     
  431.     if (err == noErr) {
  432.         err = mi_open_comm(&gStreamList, sizeof(PerStreamData), rdq, dev, flag, sflag, creds);
  433.         if ( err == noErr ) {
  434.             // Note that we can't call GetPerStreamData because the magic is not set up yet.
  435.             streamData = (PerStreamDataPtr) rdq->q_ptr;
  436.             
  437.             OTMemzero(streamData, sizeof(PerStreamData));
  438.             
  439.             streamData->magic = 'MK  ';
  440.             streamData->currentState = DL_UNATTACHED;
  441.             streamData->readQ = rdq;
  442.             streamData->http = 0;
  443.             streamData->major = getmajor(*dev);
  444.             streamData->minor = getminor(*dev);
  445.             //dopen( "MoreKitties LOG" );            
  446.  
  447.             EntryPoint("MoreKittiesOpen success\n");
  448.         }
  449.     }
  450.  
  451. done:
  452.     return (err);
  453. }
  454.  
  455. /////////////////////////////////////////////////////////////////////
  456. // Close routine
  457.  
  458. static int MoreKittiesClose(queue_t* rdq, int flags, cred_t* credP)
  459.     // This routine is called by STREAMS when a stream is being
  460.     // disconnected from our module (ie closed).  The bulk of the work
  461.     // is done by the magic Mentat helper routine mi_close_comm.
  462.     //
  463.     // Environment: standard STREAMS entry point
  464. {
  465.     #pragma unused(flags)
  466.     #pragma unused(credP)
  467.     PerStreamDataPtr     streamData;
  468.     UInt32                count;
  469.     OTAssert("MoreKittiesClose: Not the read queue", IsReadQ(rdq) );
  470.  
  471.     EntryPoint("MoreKittiesClose\n");
  472.     
  473.     streamData = GetPerStreamData(rdq);
  474.     EntryPoint("MoreKittiesClose success\n");
  475.     
  476.             
  477.     (void) mi_close_comm(&gStreamList, rdq);
  478.  
  479.     return 0;
  480. }
  481.  
  482.  
  483.  
  484. static struct module_info gModuleInfo =  
  485. {
  486.     9990,                        // Module Number, only useful for debugging
  487.     "MoreKittiesMod",                        // Name of module
  488.     0,                            // Minimum data size
  489.     1500,    // Maximum data size •••
  490.     16384,                        // Hi water mark for queue •••
  491.     4096                        // Lo water mark for queue •••
  492. };
  493.  
  494. static struct qinit gReadInit = 
  495. {
  496.     MoreKittiesReadPut,                    // Put routine for "incoming" data
  497.     nil,                        // Service routine for "incoming" data
  498.     MoreKittiesOpen,                    // Our open routine
  499.     MoreKittiesClose,                     // Our close routine
  500.     nil,                        // No admin routine
  501.     &gModuleInfo                // Our module_info
  502. };
  503.  
  504. static struct qinit gWriteInit =
  505. {
  506.     MoreKittiesWritePut,                // Put routine for client data
  507.     nil,                        // Service routine for client data
  508.     nil,                        // open  field only used in read-side structure
  509.     nil,                        // close field only used in read-side structure
  510.     nil,                        // admin field only used in read-side structure
  511.     &gModuleInfo                // Our module_info
  512. };
  513.  
  514. static struct streamtab theStreamTab = 
  515. {
  516.     &gReadInit,                    // Our read-side qinit structure
  517.     &gWriteInit,                // Our write-side qinit structure
  518.     0,                            // We are not a mux, so set this to nil
  519.     0                            // We are not a mux, so set this to nil
  520. };
  521.  
  522. /////////////////////////////////////////////////////////////////////
  523. // Macintosh-specific Static Structures
  524.  
  525. static struct install_info theInstallInfo =
  526. {
  527.     &theStreamTab,            // Stream Tab pointer
  528.     kOTModIsModule + kOTModUpperIsDLPI,
  529.                             // Tell OT that we are a module, not a driver.
  530.                             // Also set kOTModIsComplexDriver to indicate that
  531.                             // we use a configurator.
  532.     SQLVL_MODULE,            // Synchronization level, module level for the moment
  533.     0,                        // Shared writer list buddy
  534.     0,                        // Open Transport use - always set to 0
  535.     0                        // Flag - always set to 0
  536. };
  537.  
  538. /////////////////////////////////////////////////////////////////////
  539. // Prototypes for the exported routines below.
  540.  
  541. extern Boolean InitStreamModule(MoreKittiesPortData *portInfo);
  542. extern void TerminateStreamModule(void);
  543. extern install_info* GetOTInstallInfo();
  544. extern void EncryptStreams( Boolean doEncryption );
  545.  
  546. #pragma export list InitStreamModule, TerminateStreamModule, GetOTInstallInfo, SetFilter
  547.  
  548.  
  549.  
  550.  
  551. extern Boolean InitStreamModule(MoreKittiesPortData *portInfo)
  552.     // Initialises the module before the first stream is opened.
  553.     // Should return true if the module has started up correctly.
  554.     //
  555.     // Environment: Always called at SystemTask time.
  556. {    
  557.     Boolean result;
  558.  
  559.     EntryPoint("MoreKitties: InitStreamModule\n");
  560.  
  561.     // Since we've changed to a module, we don't get a valid portInfo parameter.
  562.     // For the moment, I'm leaving the assert around, just to make a point!
  563.     
  564.     // OTAssert("QTunnel: InitStreamModule: This is not the portInfo we're looking for",
  565.     //             portInfo->magic == kQTunnelPortMagic)
  566.     
  567.     #pragma unused(portInfo)
  568.     
  569.     result = true;
  570.     
  571.     return result;
  572. }
  573.  
  574. extern void TerminateStreamModule(void)
  575.     // Shuts down the module after the last stream has been
  576.     // closed.
  577.     //
  578.     // Environment: Always called at SystemTask time.
  579. {
  580.     // It's an excellent idea to have the following in your code, just to make
  581.     // sure you haven't left any streams open before you quit.  In theory, OT
  582.     // should not call you until the last stream has been closed, but in practice
  583.     // this can happen if you use mi_detach to half-close a stream.
  584.     
  585.     OTAssert("MoreKitties: TerminateStreamModule: Streams are still active", gStreamList == nil);
  586.  
  587.     EntryPoint("MoreKitties: TerminateStreamModule\n");
  588. }
  589.  
  590. extern install_info* GetOTInstallInfo()
  591.     // Return pointer to install_info to STREAMS.
  592. {
  593.     return &theInstallInfo;
  594. }
  595.